A simple example
Below is a simple hardware description from the getting started repository.
case class MyTopLevel() extends Component {
val io = new Bundle {
val cond0 = in port Bool()
val cond1 = in port Bool()
val flag = out port Bool()
val state = out port UInt(8 bits)
}
val counter = Reg(UInt(8 bits)) init 0
when(io.cond0) {
counter := counter + 1
}
io.state := counter
io.flag := (counter === 0) | io.cond1
}
It is split into chunks and explained in this section.
Component
First, there is the structure of a SpinalHDL Component.
A component is a piece of logic which can be instantiated (pasted) as many times as needed, and where the only accessible signals are its inputs and outputs.
case class MyTopLevel() extends Component {
val io = new Bundle {
// port definitions go here
}
// component logic goes here
}
MyTopLevel is the name of the component.
In SpinalHDL, components use UpperCamelCase.
Note
See also Components and hierarchy for more information.
Ports
Then, the ports are defined.
val cond0 = in port Bool()
val cond1 = in port Bool()
val flag = out port Bool()
val state = out port UInt(8 bits)
Directions:
cond0andcond1are inputs portsflagandstateare outputs ports
Types:
cond0,cond1andflagare 1 bit each (as 3 individual wires)stateis an 8-bit unsigned integer (a bus of 8 wires representing an unsigned integer)
Note
This syntax is only available since SpinalHDL 1.8, see Input / output definition for legacy syntax and more information.
Internal logic
Finally, there is the component logic:
val counter = Reg(UInt(8 bits)) init(0)
when(io.cond0) {
counter := counter + 1
}
io.state := counter
io.flag := (counter === 0) | io.cond1
counter is a register containing an 8-bits unsigned integer, with the
initial value 0. Assignments to change the state of a register are available for read-back only
after the next clock sampling.
Note
Because of the presence of a register, two implicit signals are added to the component for the clock and the reset. See Registers and Clock domains for more information.
Then a conditional rule is described: when the input cond0 (which is in the
io bundle) is set, the counter is incremented by one, else counter
keeps its value set in the last rule. But, there is no previous rule, you would
say. With a simple signal it would be a latch, and trigger an error. But here
counter is a register, so it has a default case: it just keeps the same
value.
This creates a multiplexer: the input of the counter register can be its
output or its output plus one depending on io.cond0.
Then unconditional rules (assignments) are described:
The output
stateis connected to the output of the registercounter.The output
flagis the output of anorgate between a signal which is true when the output of “counterequals 0”, and the inputcond1.
Note
See also Semantic for more information.